<!--
 * @Description: 虚拟列表
 * @Author: zhanyinjia
 * @Date: 2022-04-17 22:34:19
 * @LastEditors: zhanyinjia
 * @LastEditTime: 2022-09-28 15:35:55
-->
<template>
  <div class="viewport" ref="viewport" @scroll="handleScroll">
    <!-- 实现一个滚动条 -->
    <div class="scroll-bar" ref="scrollBar"></div>
    <!-- 数据列表 -->
    <div class="scroll-list" :style="{ transform: `translate3d(0,${offset}px,0)` }">
      <div v-for="item in caculateData" :key="item.id">
        <slot :item="item"></slot>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    remain: {
      type: Number,
      default: 8
    },
    list: {
      type: Array,
      default: () => []
    },
    size: {
      type: Number,
      default: 40
    }
  },
  data() {
    return {
      start: 0,
      end: this.remain,
      offset: 0
    }
  },
  computed: {
    //   可视区顶部预留几条数据
    prevCount() {
      return Math.min(this.start, this.remain)
    },
    // 可视区底部预渲染几条数据，防止滚动过快，底部会闪现空白
    nextCount() {
      return Math.min(this.remain, this.list.length - this.end)
    },
    // 当前需要渲染的数据
    caculateData() {
      let start = this.start - this.prevCount
      let end = this.end + this.nextCount
      return this.list.slice(start, end)
    }
  },
  mounted() {
    this.$refs.viewport.style.height = this.size * this.remain + 'px'
    this.$refs.scrollBar.style.height = this.size * this.list.length + 'px'
  },
  methods: {
    handleScroll() {
      // 1、应该先算出来当前滚过去几个了，当前应该从第几个开始显示
      // 拿到当前用户滚动的距离
      let scrollTop = this.$refs.viewport.scrollTop
      // 2、获取当前应该从第几个开始显示，假设滚动了130px，130/40=3.25，那么当前最顶部应该展示第四个数据的一半
      this.start = Math.floor(scrollTop / this.size) - this.prevCount
      // 3、计算当前结尾的位置
      this.end = this.start + this.remain
      // 定位当前可视区的位置，让当前渲染的内容在viewport的可视区内
      // 如果有预留渲染，应该把这个位置再向上移动this.size * this.prevCount这么多
      this.offset = this.start * this.size - this.size * this.prevCount
    }
  }
}
</script>

<style lang="less" scope>
.viewport {
  width: 300px;
  border: 1px solid rgb(211, 209, 209);
  overflow-y: auto;
  margin: 20px 0;
  position: relative;
  .scroll-list {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
  }
}
</style>